<!--
  Copyright (c) 2006-2013, JGraph Ltd
  
  Portsrefs example for mxGraph. This example demonstrates referencing
  connection points by ID. The main difference to the implementation
  where the connection point is stored in the connecting edge is that
  changes to the original port will be reflected in all existing
  connections since they reference that port.
-->
<html>
<head>
	<title>Port References Example</title>

	<!-- Sets the basepath for the library if not in same directory -->
	<script type="text/javascript">
		mxBasePath = '../src';
	</script>

	<!-- Loads and initializes the library -->
	<script type="text/javascript" src="../src/js/mxClient.js"></script>

	<!-- Example code -->
	<script type="text/javascript">
		function main(container)
		{
			// Replaces the port image
			mxConstraintHandler.prototype.pointImage = new mxImage('images/dot.gif', 10, 10);
			
			var graph = new mxGraph(container);
			graph.setConnectable(true);
			
			// Disables automatic handling of ports. This disables the reset of the
			// respective style in mxGraph.cellConnected. Note that this feature may
			// be useful if floating and fixed connections are combined.
			graph.setPortsEnabled(false);
			
			// Enables rubberband selection
			new mxRubberband(graph);
			
			// Gets the default parent for inserting new cells. This
			// is normally the first child of the root (ie. layer 0).
			var parent = graph.getDefaultParent();

			// Ports are equal for all shapes...
			var ports = new Array();
			
			// NOTE: Constraint is used later for orthogonal edge routing (currently ignored)
			ports['w'] = {x: 0, y: 0.5, perimeter: true, constraint: 'west'};
			ports['e'] = {x: 1, y: 0.5, perimeter: true, constraint: 'east'};
			ports['n'] = {x: 0.5, y: 0, perimeter: true, constraint: 'north'};
			ports['s'] = {x: 0.5, y: 1, perimeter: true, constraint: 'south'};
			ports['nw'] = {x: 0, y: 0, perimeter: true, constraint: 'north west'};
			ports['ne'] = {x: 1, y: 0, perimeter: true, constraint: 'north east'};
			ports['sw'] = {x: 0, y: 1, perimeter: true, constraint: 'south west'};
			ports['se'] = {x: 1, y: 1, perimeter: true, constraint: 'south east'};

			// ... except for triangles
			var ports2 = new Array();
			
			// NOTE: Constraint is used later for orthogonal edge routing (currently ignored)
			ports2['in1'] = {x: 0, y: 0, perimeter: true, constraint: 'west'};
			ports2['in2'] = {x: 0, y: 0.25, perimeter: true, constraint: 'west'};
			ports2['in3'] = {x: 0, y: 0.5, perimeter: true, constraint: 'west'};
			ports2['in4'] = {x: 0, y: 0.75, perimeter: true, constraint: 'west'};
			ports2['in5'] = {x: 0, y: 1, perimeter: true, constraint: 'west'};

			ports2['out1'] = {x: 0.5, y: 0, perimeter: true, constraint: 'north east'};
			ports2['out2'] = {x: 1, y: 0.5, perimeter: true, constraint: 'east'};
			ports2['out3'] = {x: 0.5, y: 1, perimeter: true, constraint: 'south east'};
			
			// Extends shapes classes to return their ports
			mxShape.prototype.getPorts = function()
			{
				return ports;
			};

			mxTriangle.prototype.getPorts = function()
			{
				return ports2;
			};

			// Disables floating connections (only connections via ports allowed)
			graph.connectionHandler.isConnectableCell = function(cell)
			{
			   return false;
			};
			mxEdgeHandler.prototype.isConnectableCell = function(cell)
			{
				return graph.connectionHandler.isConnectableCell(cell);
			};
			
			// Disables existing port functionality
			graph.view.getTerminalPort = function(state, terminal, source)
			{
				return terminal;
			};
			
			// Returns all possible ports for a given terminal
			graph.getAllConnectionConstraints = function(terminal, source)
			{
				if (terminal != null && terminal.shape != null &&
					terminal.shape.stencil != null)
				{
					// for stencils with existing constraints...
					if (terminal.shape.stencil != null)
					{
						return terminal.shape.stencil.constraints;
					}
				}
				else if (terminal != null && this.model.isVertex(terminal.cell))
				{
					if (terminal.shape != null)
					{
						var ports = terminal.shape.getPorts();
						var cstrs = new Array();
						
						for (var id in ports)
						{
							var port = ports[id];
							
							var cstr = new mxConnectionConstraint(new mxPoint(port.x, port.y), port.perimeter);
							cstr.id = id;
							cstrs.push(cstr);
						}
						
						return cstrs;
					}
				}
				
				return null;
			};
			
			// Sets the port for the given connection
			graph.setConnectionConstraint = function(edge, terminal, source, constraint)
			{
				if (constraint != null)
				{
					var key = (source) ? mxConstants.STYLE_SOURCE_PORT : mxConstants.STYLE_TARGET_PORT;
					
					if (constraint == null || constraint.id == null)
					{
						this.setCellStyles(key, null, [edge]);
					}
					else if (constraint.id != null)
					{
						this.setCellStyles(key, constraint.id, [edge]);
					}
				}
			};
			
			// Returns the port for the given connection
			graph.getConnectionConstraint = function(edge, terminal, source)
			{
				var key = (source) ? mxConstants.STYLE_SOURCE_PORT : mxConstants.STYLE_TARGET_PORT;
				var id = edge.style[key];
				
				if (id != null)
				{
					var c =  new mxConnectionConstraint(null, null);
					c.id = id;
					
					return c;
				}
				
				return null;
			};

			// Returns the actual point for a port by redirecting the constraint to the port
			graphGetConnectionPoint = graph.getConnectionPoint;
			graph.getConnectionPoint = function(vertex, constraint)
			{
				if (constraint.id != null && vertex != null && vertex.shape != null)
				{
					var port = vertex.shape.getPorts()[constraint.id];
					
					if (port != null)
					{
						constraint = new mxConnectionConstraint(new mxPoint(port.x, port.y), port.perimeter);
					}
				}
				
				return graphGetConnectionPoint.apply(this, arguments);
			};
				
			// Adds cells to the model in a single step
			graph.getModel().beginUpdate();
			try
			{
				var v1 = graph.insertVertex(parent, null, 'A', 20, 20, 100, 40);
				var v2 = graph.insertVertex(parent, null, 'B', 80, 100, 100, 100,
						'shape=ellipse;perimeter=ellipsePerimeter');
				var v3 = graph.insertVertex(parent, null, 'C', 190, 30, 100, 60,
						'shape=triangle;perimeter=trianglePerimeter;direction=south');
				var e1 = graph.insertEdge(parent, null, '', v1, v2, 'sourcePort=s;targetPort=nw');
				var e2 = graph.insertEdge(parent, null, '', v1, v3, 'sourcePort=e;targetPort=out3');
			}
			finally
			{
				// Updates the display
				graph.getModel().endUpdate();
			}
			
			// Comming soon... Integration with orthogonal edge style
			// Sets default edge style to use port constraints (needs to be moved up when uncommented)
			//graph.getStylesheet().getDefaultEdgeStyle()['edgeStyle'] = 'orthogonalEdgeStyle';
			/*var mxUtilsGetPortConstraints = mxUtils.getPortConstraints;
			mxUtils.getPortConstraints = function(terminal, edge, source, defaultValue)
			{
				var key = (source) ? mxConstants.STYLE_SOURCE_PORT : mxConstants.STYLE_TARGET_PORT;
				var id = edge.style[key];
				
				var port = terminal.shape.getPorts()[id];
				
				// TODO: Add support for rotation, direction
				if (port != null)
				{
					return port.constraint;
				}
				
				return mxUtilsGetPortConstraints.apply(this, arguments);
			};
			// Connect preview
			graph.connectionHandler.createEdgeState = function(me)
			{
				var edge = graph.createEdge(null, null, null, null, null);
				
				return new mxCellState(this.graph.view, edge, this.graph.getCellStyle(edge));
			};
			*/
		};
	</script>
</head>
<body onload="main(document.getElementById('graphContainer'))">
	<div id="graphContainer"
		style="overflow:hidden;position:relative;width:321px;height:241px;background:url('editors/images/grid.gif');cursor:default;">
	</div>
</body>
</html>
